<?php
/**
Raid object serialization functions.

'Object' is a bit of a misnomer as the raid is actually represented as array.

raid_object_load creats a logical representation of a raid from the timeline
raid_object_save reads from a logical raid object and writes it's contents back into the timeline

Some optimization needs to be done on this.  Loading the timeline array requires 85% of this function's
average execution time, mainly because of the massive number of calls to node_load.  This information needs
to be condensed into the single query at the top.  I believe this will speed up execution by over 6 times.
**/
function _mewdkp_raid_object_load(&$node) {  
  //for debug purposes, record script execution time for building thiso bject.
  $time_start = microtime(true);
  
  //Uses timeline view for data parsing
  static $timeline_view;
  if (empty($timeline_view)) { //Statically cache the view object.  Investigating why node_load hangs build().
    $timeline_view = views_get_view('mewdkp_timeline', TRUE);
    $timeline_view->set_arguments(array($node->nid));
    $timeline_view->build();
  }
  else {
    return FALSE;
  }
  $result = db_query($timeline_view->build_info['query'], $timeline_view->build_info['query_args']);
  unset($timeline_view); //View objects can be big
  $timeline = array();
  while ($row = db_fetch_object($result)) {
    $timeline[] = $row;
  }

  $raid = array();
  
  // Build raid array object from timeline
  $raid['kills'] = array();
  
  $attendance = array(); //Keeps track of current raid makeup as the events traverse
  
  
    
  foreach ($timeline as &$row) {  
    switch ($row->node_data_field_mewdkp_timeline_type_field_mewdkp_timeline_type_value) {
      case 4: //Handle player leave event        
        $k = array_search($row->node_data_field_mewdkp_player_field_mewdkp_player_nid, $attendance);
        if ($k) {
          unset($attendance[$k]);
        }
        break;
      case 3: //Handle player join event
        if (!in_array($row->node_data_field_mewdkp_player_field_mewdkp_player_nid, $attendance)) {
          $attendance[] = $row->node_data_field_mewdkp_player_field_mewdkp_player_nid;
        }
        break;
      case 1:  //Handle boss kill events
        $mob = node_load($row->node_data_field_mewdkp_mob_field_mewdkp_mob_nid);
        
        $bosskill = array();        
        $bosskill['name'] = $mob->title;
        $bosskill['nid'] = $mob->nid;
        $bosskill['attendance'] = $attendance;
        
        $bosskill['drops'] = array();
        
        $raid['kills'][] = $bosskill;
        
        break;
      case 2: //Handle item looting events

        $mob = node_load($row->node_data_field_mewdkp_mob_field_mewdkp_mob_nid);
        $item = node_load($row->node_node_data_field_mewdkp_item_nid);
          
        //Handle kill event generation for trash mobs
        if ($mob->title == "Trash mob") {
          $newkill = FALSE;
          
          //Find last trash kill event if one exists.  If not, create one.  If it does, compare it to current attendance and create new entry if it's different.
          $i = count($raid['kills']) - 1;  
          if ($i >= 0) {
            //compare attendance arrays
            $atn_change = array_diff($raid['kills'][$i]['attendance'], $attendance);
            
            if (count($atn_change) > 0 || $raid['kills'][$i]['name'] != "Trash mob") {
              //If there's a difference, a new trash kill has to be generated
              $newkill = TRUE;
            }
          }
          else {
            //This is the first recorded item drop
            $newkill = TRUE;
          }
          
          if ($newkill) {
            //Create new kill event for trash mob
            $trashkill = array();
            
            $trashkill['name'] = $mob->title;
            $trashkill['nid'] = $mob->nid;  
            $trashkill['attendance'] = $attendance;
            
            $trashkill['drops'] = array();
            
            //Create new kill in the array and assign $i to it's index
            $raid['kills'][] = $trashkill;
            $i = count($raid['kills'])-1;
          }
          
          $drop = array();
          $drop['name'] = $item->title;
          $drop['nid'] = $item->nid;
          $drop['wowid'] = $item->field_mewdkp_wowid[0]['value'];
          $drop['winner'] = $row->node_data_field_mewdkp_player_field_mewdkp_player_nid;
          $drop['count'] = $row->node_data_field_mewdkp_timeline_type_field_mewdkp_count_value;
          $drop['note'] = $row->node_data_field_mewdkp_timeline_type_field_mewdkp_note_value;
          $drop['timeline_nid'] = $row->nid;
          
          $raid['kills'][$i]['drops'][] = $drop;
        }
        //If droppedfrom is not Trash mob
        else {
          //Find correct boss kill to give this to
          $i = 0;
          $newkill = FALSE;
          
          while (($i < count($raid['kills'])) && ($raid['kills'][$i]['name'] != $mob->title)) $i++;
          
          //Use name from "droppedfrom" field to generate new kill event
          if ($i >= count($raid['kills'])) {
            $newkill = TRUE;
          }
          
          if ($newkill) {      
            $bosskill['name'] = $mob->title;
            $bosskill['nid'] = $mob->nid;
            $bosskill['attendance'] = $attendance;
            
            $bosskill['drops'] = array();
            
            $raid['kills'][] = $bosskill;
            
            $i = count($raid['kills'])-1;
          }            
          
          $drop = array();
          $drop['name'] = $item->title;
          $drop['nid'] = $item->nid;
          $drop['wowid'] = $item->field_mewdkp_wowid[0]['value'];
          $drop['winner'] = $row->node_data_field_mewdkp_player_field_mewdkp_player_nid;
          $drop['count'] = $row->node_data_field_mewdkp_timeline_type_field_mewdkp_count_value;
          $drop['note'] = $row->node_data_field_mewdkp_timeline_type_field_mewdkp_note_value;
          $drop['timeline_nid'] = $row->nid;
          
          $raid['kills'][$i]['drops'][] = $drop;
        }
        break;
      default:
        break;
    }
  }

  $time = microtime(true) - $time_start;  //Just records foreach loop
  $raid['object_build_time'] = $time;  
  
  return $raid;
}

/**
 * Helper function for saving the contents of a raid object to the timeline
 *
 * @param $node A fully constructed raid node object
 * @return int Number of new timeline events created if valid, FALSE if validation failed.
 **/
function _mewdkp_raid_object_save(&$node){
  if ($node->type != 'mewdkp_raid') {
    return FALSE;
  }
  elseif (!is_array($node->kills)) {
    return FALSE;
  }
  //TO DO: Some object integrity validation here might be nice

  //Delete timeline events for given raid
  _mewdkp_remove_raid($node);
  
  $attendance = array();  
  $order = 0;
  
  foreach ($node->kills as $key => $kill){  
    //Handle join events
    
    $a_sect = array_intersect($attendance, $kill['attendance']); // <- Gets all players who are present in both records.
    $joins = array_diff($kill['attendance'], $a_sect);
    $leaves = array_diff($attendance, $a_sect);
    
    $mob_node = node_load($kill['nid']);
    
    //Insert leave events
    foreach ($leaves as $player_nid){
      $order++;
      
      $left_player = node_load($player_nid);
      
      $timeline_node = new stdClass();
      $timeline_node->type = 'mewdkp_timeline';
      $timeline_node->field_mewdkp_raid[0]['nid'] = $node->nid;
      $timeline_node->field_mewdkp_timeline_type[0]['value'] = 4;
      $timeline_node->field_mewdkp_event_order[0]['value'] = $order;      
      $timeline_node->field_mewdkp_player[0]['nid'] = $left_player->nid;
      $timeline_node->field_mewdkp_mob[0]['nid'] = 0;
      $timeline_node->field_mewdkp_item[0]['nid'] = 0;
      
      $timeline_node->title =  'Player Leave - '. $left_player->title;
      node_save($timeline_node);
    }
    
    //Insert join events
    foreach ($joins as $player_nid) {
      $order++;
      
      $joined_player = node_load($player_nid);
      
      $timeline_node = new stdClass();
      $timeline_node->type = 'mewdkp_timeline';
      $timeline_node->field_mewdkp_raid[0]['nid'] = $node->nid;
      $timeline_node->field_mewdkp_timeline_type[0]['value'] = 3;
      $timeline_node->field_mewdkp_event_order[0]['value'] = $order;      
      $timeline_node->field_mewdkp_player[0]['nid'] = $joined_player->nid;
      $timeline_node->field_mewdkp_mob[0]['nid'] = 0;
      $timeline_node->field_mewdkp_item[0]['nid'] = 0;
      
      $timeline_node->title =  'Player Join - '. $joined_player->title;
      node_save($timeline_node);    
    }
    
    //Insert boss kill event
    if ($kill['name'] != 'Trash mob') {
      $order++;
      
      $timeline_node = new stdClass();
      $timeline_node->type = 'mewdkp_timeline';
      $timeline_node->field_mewdkp_raid[0]['nid'] = $node->nid;
      $timeline_node->field_mewdkp_timeline_type[0]['value'] = 1;
      $timeline_node->field_mewdkp_event_order[0]['value'] = $order;      
      $timeline_node->field_mewdkp_player[0]['nid'] = 0;
      $timeline_node->field_mewdkp_mob[0]['nid'] = $mob_node->nid;
      $timeline_node->field_mewdkp_item[0]['nid'] = 0;
      
      $timeline_node->title = 'Mob Kill - '. $mob_node->title;
      node_save($timeline_node);    
    }
    
    //Insert drops    
    foreach ($kill['drops'] as $drop){
      $order++;
      
      $item_node = node_load($drop['nid']);
      
      $timeline_node = new stdClass();
      $timeline_node->type = 'mewdkp_timeline';
      $timeline_node->field_mewdkp_raid[0]['nid'] = $node->nid;
      $timeline_node->field_mewdkp_timeline_type[0]['value'] = 2;
      $timeline_node->field_mewdkp_event_order[0]['value'] = $order;      
      $timeline_node->field_mewdkp_player[0]['nid'] = $drop['winner'];
      $timeline_node->field_mewdkp_mob[0]['nid'] = $mob_node->nid;
      $timeline_node->field_mewdkp_item[0]['nid'] = $item_node->nid;
      $timeline_node->field_mewdkp_count[0]['value'] = ($drop['count'] > 0)?($drop['count']):(1);
      $timeline_node->field_mewdkp_note[0]['value'] = $drop['note'];
      
      $timeline_node->title = 'Loot Item - '. $item_node->title;
      node_save($timeline_node);
      
    }
    
    //Update attendance array so the next iteration can reference it for player joins / leaves.
    $attendance = $kill['attendance'];
  }
  
  //Clean up, add remaining leave events
  foreach ($attendance as $player_nid){
    $order++;
    
    $left_player = node_load($player_nid);
    
    $timeline_node = new stdClass();
    $timeline_node->type = 'mewdkp_timeline';
    $timeline_node->field_mewdkp_raid[0]['nid'] = $node->nid;
    $timeline_node->field_mewdkp_timeline_type[0]['value'] = 4;
    $timeline_node->field_mewdkp_event_order[0]['value'] = $order;
    
    $timeline_node->field_mewdkp_player[0]['nid'] = $left_player->nid;
    $timeline_node->field_mewdkp_mob[0]['nid'] = 0;
    $timeline_node->field_mewdkp_item[0]['nid'] = 0;
    
    $timeline_node->title =  'Player Leave - '. $left_player->name;
    node_save($timeline_node);
  }
  
  return $order;
}